﻿using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;

namespace EEG_NIRS_Record
{
    public partial class TopoForm : Form
    {
        MainForm _mainform;
        int _showtype;

        Graphics graphics;
        SolidBrush brush_bg;
        Point center;
        int radius;
        Region reg;
        int x0;
        int y0;
        int num_of_ch;
        List<Point> list;
        Matrix<double> d;  // Determine distances between points
        Matrix<double> gg;
        double[,] vq;

        System.Timers.Timer timer;
        
        public TopoForm(MainForm mainform, int showtype)
        {
            InitializeComponent();

            _mainform = mainform;
            _showtype = showtype;

            center = _mainform.configuration.list[45];
            radius = (int)(_mainform.configuration.list[31].Y - _mainform.configuration.list[45].Y);
            GraphicsPath gp = new GraphicsPath();
            Rectangle rc = _mainform.configuration.getRectangle(center, radius);
            gp.AddEllipse(rc);
            reg = new Region(gp);
            reg.Complement(rc);
            x0 = center.X - radius;
            y0 = center.Y - radius;

            brush_bg = new SolidBrush(Color.BurlyWood);

            timer = new System.Timers.Timer();
        }

        private void TopoForm_Load(object sender, EventArgs e)
        {
            Bitmap image = new Bitmap(pb_topo.Width, pb_topo.Height);
            pb_topo.Image = image;
            graphics = Graphics.FromImage(image);
            //高质量低速度呈现 指定是否将平滑处理（消除锯齿）应用于直线、曲线和已填充区域的边缘。
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            graphics.SmoothingMode = SmoothingMode.HighQuality;

            //////
            if (_showtype == 0)
            {
                num_of_ch = _mainform.configuration.eeg_list.Count;
                list = _mainform.configuration.eeg_list.ToList();
            }
            else
            {
                num_of_ch = _mainform.configuration.nirs_list.Count;
                list = _mainform.configuration.nirs_list.ToList();
            }
            d = new DenseMatrix(num_of_ch, num_of_ch);
            for (int i = 0; i < num_of_ch; i++)
            {
                for (int j = 0; j < num_of_ch; j++)
                {
                    d[i, j] = _mainform.configuration.DistanceTo(list[i], list[j]);
                }
            }
            gg = d.PointwisePower(2.0).PointwiseMultiply((d.PointwiseLog() - 1));
            for (int i = 0; i < num_of_ch; i++)
            {
                gg[i, i] = 0.0;
            }

            vq = new double[2 * radius, 2 * radius];  // 1个点画一个
            //vq = new double[radius, radius];   // 4个点画一个

            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Interval = 1000;
            timer.Start();
            //ShowTopoGraphs(_mainform.eeg_data_for_topo);

            //Random rd = new Random();
            //double[] ddd = new double[32];
            //for (int i = 0; i < 32; i++)
            //{
            //    ddd[i] = rd.NextDouble();
            //}

            //System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            //stopwatch.Start();
            //ShowTopoGraphs(ddd);
            //stopwatch.Stop();
            //TimeSpan timespan = stopwatch.Elapsed;
            //Console.WriteLine(timespan.TotalMilliseconds);
        }
        private void DrawBrain(Graphics g)
        {
            Pen pen_brain = new Pen(Color.Black, 2);
            SolidBrush brush_elec = new SolidBrush(Color.Black);
            
            // draw brain borad
            g.DrawEllipse(pen_brain, _mainform.configuration.getRectangle(center, radius));
            g.DrawEllipse(pen_brain, _mainform.configuration.getRectangle(center, (int)(_mainform.configuration.list[45].Y - _mainform.configuration.list[0].Y)));
            // draw ears
            int ear_width = 20;
            int ear_height = 50;
            g.DrawEllipse(pen_brain, new Rectangle((int)center.X - radius - ear_width, (int)center.Y - ear_height, ear_width, 2 * ear_height));
            g.DrawEllipse(pen_brain, new Rectangle((int)center.X + radius, (int)center.Y - ear_height, ear_width, 2 * ear_height));
            // draw nose
            Point ln = _mainform.configuration.RotatePoint(new Point(center.X, center.Y - radius), center, Math.PI / 22, false);
            Point rn = _mainform.configuration.RotatePoint(new Point(center.X, center.Y - radius), center, Math.PI / 22);
            Point tip = new Point(center.X, center.Y - radius - 40);
            g.DrawLine(pen_brain, (int)tip.X, (int)tip.Y, (int)ln.X, (int)ln.Y);
            g.DrawLine(pen_brain, (int)tip.X, (int)tip.Y, (int)rn.X, (int)rn.Y);
            ////
            g.DrawLine(pen_brain, (int)center.X, (int)center.Y + radius, (int)center.X, (int)center.Y - radius);
            g.DrawLine(pen_brain, (int)center.X + radius, (int)center.Y, (int)center.X - radius, (int)center.Y);

            int probe_radius = 3;
            if (_showtype == 0)
            {
                //Font font = new Font("宋体", 7);
                for (int i = 0; i < _mainform.configuration.eeg_list.Count; i++)
                {
                    g.FillEllipse(brush_elec, _mainform.configuration.getRectangle((int)_mainform.configuration.eeg_list[i].X, (int)_mainform.configuration.eeg_list[i].Y, probe_radius));
                    //g.DrawString(probe_name[i], font, Brushes.Black, (int)list[i].x - ele_radius + 2, (int)list[i].y - 3);
                }
            }
            else
            {
                //Font font = new Font("宋体", 7);
                for (int i = 0; i < _mainform.configuration.nirs_list.Count; i++)
                {
                    g.FillEllipse(brush_elec, _mainform.configuration.getRectangle((int)_mainform.configuration.nirs_list[i].X, (int)_mainform.configuration.nirs_list[i].Y, probe_radius));
                    //g.DrawString(probe_name[i], font, Brushes.Black, (int)list[i].x - ele_radius + 2, (int)list[i].y - 3);
                }
            }            
        }
        public void ShowTopoGraph(double[] data)  // 1个点画一个
        {
            
            double vq_max = 0.0;
            double vq_min = 0.0;
            var mb = Matrix<double>.Build;
            var v = mb.DenseOfColumnArrays(data);
            Matrix<double> weights = gg.Inverse() * v;
            var d = mb.Dense(1, num_of_ch);
            for (int i = 0; i < 2 * radius; i++)
            {
                for (int j = 0; j < 2 * radius; j++)
                {
                    if (reg.IsVisible(x0 + i, y0 + j))
                    {
                        for (int k = 0; k < num_of_ch; k++)
                        {
                            d[0, k] = _mainform.configuration.DistanceTo(x0 + i, y0 + j, list[k]);
                        }
                        var g = d.PointwisePower(2.0).PointwiseMultiply(d.PointwiseLog() - 1);
                        vq[i, j] = (g * weights)[0, 0];
                        if (vq[i, j] > vq_max)
                        {
                            vq_max = vq[i, j];
                        }
                        if (vq[i, j] < vq_min)
                        {
                            vq_min = vq[i, j];
                        }
                    }
                }
            }

            graphics.Clear(Color.Transparent);
            ColorMap cm = new ColorMap();
            int[,] cmap = cm.Jet();
            int colorLength = cmap.GetLength(0);
            for (int i = 0; i < 2 * radius; i++)
            {
                for (int j = 0; j < 2 * radius; j++)
                {
                    if (reg.IsVisible(x0 + i, y0 + j))
                    {
                        int cindex = (int)Math.Round(colorLength * (vq[i, j] - vq_min) / (vq_max - vq_min));
                        if (cindex < 1)
                        {
                            cindex = 1;
                        }
                        if (cindex > colorLength)
                        {
                            cindex = colorLength;
                        }
                        ((Bitmap)pb_topo.Image).SetPixel(x0 + i, y0 + j, Color.FromArgb(cmap[cindex - 1, 0], cmap[cindex - 1, 1], cmap[cindex - 1, 2], cmap[cindex - 1, 3]));                        
                    }
                }
            }

            DrawBrain(graphics);
            pb_topo.Invalidate();
        }
        public void ShowTopoGraphs(double[] data)  // 4个点画一个
        {
            double vq_max = 0.0;
            double vq_min = 0.0;
            var mb = Matrix<double>.Build;
            var v = mb.DenseOfColumnArrays(data);
            Matrix<double> weights = gg.Inverse() * v;
            var d = mb.Dense(1, num_of_ch);
            for (int i = 0; i < radius; i++)
            {
                for (int j = 0; j < radius; j++)
                {
                    //if (reg.IsVisible(x0 + i * 2, y0 + j * 2))
                    //{
                        for (int k = 0; k < num_of_ch; k++)
                        {
                            d[0, k] = _mainform.configuration.DistanceTo(x0 + i * 2, y0 + j * 2, list[k]);
                        }
                        var g = d.PointwisePower(2.0).PointwiseMultiply(d.PointwiseLog() - 1);
                        vq[i, j] = (g * weights)[0, 0];
                        if (vq[i, j] > vq_max)
                        {
                            vq_max = vq[i, j];
                        }
                        if (vq[i, j] < vq_min)
                        {
                            vq_min = vq[i, j];
                        }
                    //}
                }
            }

            graphics.Clear(Color.Transparent);
            ColorMap cm = new ColorMap();
            int[,] cmap = cm.Jet();
            int colorLength = cmap.GetLength(0);
            for (int i = 0; i < radius; i++)
            {
                for (int j = 0; j < radius; j++)
                {
                    //if (reg.IsVisible(x0 + i * 2, y0 + j * 2))
                    //{
                        int cindex = (int)Math.Round(colorLength * (vq[i, j] - vq_min) / (vq_max - vq_min));
                        if (cindex < 1)
                        {
                            cindex = 1;
                        }
                        if (cindex > colorLength)
                        {
                            cindex = colorLength;
                        }
                        graphics.FillRectangle(new SolidBrush(Color.FromArgb(cmap[cindex - 1, 0], cmap[cindex - 1, 1], cmap[cindex - 1, 2], cmap[cindex - 1, 3])), x0 + i * 2, y0 + j * 2, 2, 2);
                    //}
                }
            }
            graphics.FillRegion(brush_bg, reg);
            DrawBrain(graphics);
            pb_topo.Invalidate();
        }

        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            //Random rd = new Random();
            //double[] ddd = new double[32];
            //for (int i = 0; i < 32; i++)
            //{
            //    ddd[i] = rd.NextDouble();
            //}
            //System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            //stopwatch.Start();
            //ShowTopoGraphs(ddd);
            //stopwatch.Stop();
            //TimeSpan timespan = stopwatch.Elapsed;
            //Console.WriteLine(timespan.TotalMilliseconds);
            double[] data = null;
            lock (_mainform.eeg_data_for_topo)
            {
                data = _mainform.eeg_data_for_topo.ToArray();
            }
            if (_showtype == 0)
            {
                ShowTopoGraphs(data);
            }
            else
            {
                ShowTopoGraphs(data);
            }
        }

        private void TopoForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            timer.Stop();
        }
    }
}
